#pragma once
#include <brpc/channel.h>
#include <string.h>
#include <memory.h>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <mutex>
#include "logger.hpp"

//1.封装单个服务的信道管理类：建立服务器与信道的映射关系(一对多)
class ServiceChannel
{
public:
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    using ptr = std::shared_ptr<ServiceChannel>;

    ServiceChannel(const std::string& serviceName)
        :_serviceName(serviceName)
        ,_index(0)
    {}

    //服务新增一个节点主机，构建一个信道
    void AddChannel(const std::string& host)
    {
        auto channel = std::make_shared<brpc::Channel>(); // 构造信道
        brpc::ChannelOptions options;
        options.connect_timeout_ms = -1; // 连接等待超时时间，-1表示一直等待
        options.timeout_ms = -1;         // rpc请求等待超时时间，-1表示一直等待
        options.max_retry = 3;           // 请求重试次数
        options.protocol = "baidu_std";  // 序列化协议，默认使用baidu_std
        int ret = channel->Init(host.c_str(),&options);
        if (ret == -1)
        {
            LOG_ERROR("初始化{}--{}信道失败!",_serviceName,host);
            return;
        }
        //维护信道
        std::unique_lock<std::mutex> lock(_mutex);
        _hostsHash.insert(std::make_pair(host,channel));
        _channels.push_back(channel);
    }

    //服务下线一个节点主机，删除一个信道
    void RemoveChannel(const std::string &host)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _hostsHash.find(host);
        if (it == _hostsHash.end())
        {
            LOG_WARN("{}-{}节点删除信道时，没有找到信道信息！", _serviceName, host);
            return;
        }
        for (auto vit = _channels.begin(); vit != _channels.end(); ++vit)
        {
            if (*vit == it->second)
            {
                _channels.erase(vit);
                break;
            }
        }
        _hostsHash.erase(it);
    }

    //通过RR轮转测录，获取一个信道，用于发起对应服务的rpc调用
    ChannelPtr chooseChannel()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_channels.size() == 0)
        {
            LOG_ERROR("当前没有能够提供 {} 服务的节点！", _serviceName);
            return ChannelPtr();
        }
        int32_t idx = _index++ % _channels.size();
        return _channels[idx];
    }

private:
    std::mutex _mutex;
    int32_t _index;                     //当前轮转计数器下标
    std::string _serviceName;           //服务名称
    std::vector<ChannelPtr> _channels;  //当前服务对应的信道集合
    std::unordered_map<std::string,ChannelPtr> _hostsHash;  //主机地址与信道的映射关系，方便查找服务的信道
};


//总体的服务信道管理类
class ServiceChannelManager
{
public:
    using ptr = std::shared_ptr<ServiceChannelManager>;

    //获取指定服务的一个信道
    ServiceChannel::ChannelPtr choose(const std::string &serviceName)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _services.find(serviceName);
        if(it == _services.end())
        {
            LOG_ERROR("当前没有能够提供 {} 服务的节点！", serviceName);
            return ServiceChannel::ChannelPtr();
        }
        //获取服务的信道
        return it->second->chooseChannel();
    }

    //先声明，我关注哪些服务的上下线，不关心的就不需要管理了
    //没必要把所有服务的连接都建立起来
    //关心的管理，不关心的不管理
    void declared(const std::string serviceName)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _careService.insert(serviceName);

    }

    // 服务上线时调用的回调接口，从服务信道管理中，添加指定节点信道管理，供ectd使用
    void onServiceUpline(const std::string &service_instance, const std::string &host)
    {
        std::string serviceName = getServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto careIt = _careService.find(serviceName);
            if (careIt == _careService.end())
            {
                LOG_ERROR("{}--{}服务上线了一个节点，但是不关心", serviceName, host);
                return;
            }

            // 关心该服务，先获取服务信道的管理对象：没有则创建，有则添加节点
            auto serviceIt = _services.find(serviceName);
            if (serviceIt == _services.end())
            {
                //单个服务信道管理类
                service = std::make_shared<ServiceChannel>(serviceName);   

                _services.insert(std::make_pair(serviceName, service));
            }
            else
            {
                service = serviceIt->second;
            }
        }
        if(!service)
        {
            LOG_ERROR("新增{}服务管理节点失败", serviceName);
            return;
        }
        service->AddChannel(host);
        LOG_ERROR("{}--{}服务上线了一个新节点", serviceName, host);
    }

    // 服务下线时调用的回调接口，从服务信道管理中，删除指定节点信道，供ectd使用
    void onServiceDownline(const std::string &service_instance, const std::string &host)
    {
        std::string serviceName = getServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto careIt = _careService.find(serviceName);
            if (careIt == _careService.end())
            {
                LOG_DEBUG("{}-{} 服务下线了，但是当前并不关心！", serviceName, host);
                return;
            }
            auto sit = _services.find(serviceName);
            if (sit == _services.end())
            {
                LOG_WARN("删除{}服务节点时，没有找到管理对象", serviceName);
                return;
            }
            service = sit->second;
        }
        service->RemoveChannel(host);
        LOG_DEBUG("{}-{} 服务下线节点，进行删除管理！", serviceName, host);
    }

private:
    std::string getServiceName(const std::string &service_instance)
    {
        auto pos = service_instance.find_last_of('/');
        if (pos == std::string::npos)
            return service_instance;
        return service_instance.substr(0, pos);
    }

private:
    std::mutex _mutex;
    std::unordered_set<std::string> _careService;
    std::unordered_map<std::string,ServiceChannel::ptr> _services;  //<sercvice,channelVecotr>
};